﻿/*****************************************************************************
 * Hyldahl.Services.TaskProcessing                 Created by: MSH 2008.07.22
 * --------------------------------------------------------------------------
 * FileName: TaskProcessingController.cs
 * --------------------------------------------------------------------------
 * Copyright 2009 Martin Sixhøj Hyldahl
 *****************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Hyldahl.Services.TaskProcessing.Configuration;
using Hyldahl.Services.TaskProcessing.Workers;

namespace Hyldahl.Services.TaskProcessing
{
    public class TaskProcessingController : System.IDisposable
    {
        /*****************************************************
         * FIELDS
         *****************************************************/
        private TaskProcessingContext   context;
        private List<Worker>            workers;
        private TraceSource             traceSource;
        protected bool                  initialized;

        /*****************************************************
         * CONSTRUCTOR
         *****************************************************/

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskProcessingController"/> class.
        /// </summary>
        public TaskProcessingController(TaskProcessingContext context)
        {
            this.context    = context;
            traceSource     = new TraceSource("TaskProcessors");
        }

        /*****************************************************
         * METHODS
         *****************************************************/

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public void Initialize()
        {
            this.traceSource.TraceInformation("Initializing");

            workers = new List<Worker>();

            // SETUP WORKERS
            int threadCount = TaskProcessingServiceConfiguration.Configuration.TaskProcessingThreads.NumberOfThreads;

            if (threadCount < 1)
                threadCount = 1;

            for (int idx = 0; idx < threadCount; idx++)
                workers.Add(new TaskProcessingWorker(this, "TPW_" + idx.ToString()));

            initialized = true;

            this.traceSource.TraceInformation("Initialized");
        }

        /// <summary>
        /// Starts the workers.
        /// </summary>
        public void StartWorkers()
        {
            if(!initialized)
                Initialize();

            this.traceSource.TraceInformation("Starting threads");
            this.traceSource.TraceInformation(string.Format("{0} worker(s)", workers.Count));

            foreach (Worker w in workers)
                w.Start();
        }

        /// <summary>
        /// Stops the workers.
        /// </summary>
        public void StopWorkers()
        {
            this.traceSource.TraceInformation("Stopping threads");

            foreach (Worker c in workers)
                if ((c.State & WorkerState.Started) == WorkerState.Started ||
                    (c.State & WorkerState.Paused) == WorkerState.Paused)
                    c.Stop();
        }

        /// <summary>
        /// Waits for completion.
        /// </summary>
        public void WaitForCompletion()
        {
            WaitForCompletion(TimeSpan.Zero);
        }

        /// <summary>
        /// Waits for completion.
        /// </summary>
        public void WaitForCompletion(TimeSpan workerTimeout)
        {
            // SET STOP FLAG FOR ALL RUNNING WORKERS
            foreach (Worker w in workers)
            {
                if ((w.State & WorkerState.Started)  == WorkerState.Started ||
                    (w.State & WorkerState.Paused)   == WorkerState.Paused)
                {
                    w.Stop();
                }
            }

            // KILL WORKERS IF THEY DO NOT FINISH WITHIN TIMEOUT
            foreach (Worker w in workers)
            {
                if ((w.State & WorkerState.Started)  == WorkerState.Started ||
                    (w.State & WorkerState.Paused)   == WorkerState.Paused)
                {
                    if (!w.WaitForCompletion(workerTimeout))
                    {
                        this.traceSource.TraceEvent(TraceEventType.Error, 0, string.Format("Worker: {0} did not finished in the allowed timeframe {1}.", w.Name, workerTimeout));

                        try
                        {
                            w.Kill();
                        }
                        catch
                        {
                            // Swallow exceptions when killing threads.
                        }
                    }
                }
            }

            this.traceSource.TraceInformation("All threads stopped");
        }

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            StopWorkers();
            WaitForCompletion(new TimeSpan(0, 0, 15));

            GC.SuppressFinalize(this);
        }

        #endregion

        /*****************************************************
         * PRIVATE METHODS
         *****************************************************/

        /*****************************************************
         * PROPERTIES
         *****************************************************/

        /// <summary>
        /// Gets the trace source.
        /// </summary>
        /// <value>The trace source.</value>
        public TraceSource TraceSource
        {
            get { return traceSource; }
        }

        /// <summary>
        /// Gets the active workers.
        /// </summary>
        /// <value>The active workers.</value>
        public int ActiveWorkers
        {
            get
            {
                int count = 0;

                foreach (Worker c in workers)
                    if ((c.State & WorkerState.Started) == WorkerState.Started)
                        count++;

                return count;
            }
        }

        /// <summary>
        /// Gets the workers.
        /// </summary>
        /// <value>The workers.</value>
        public List<Worker> Workers
        {
            get
            {
                return workers;
            }
        }

        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <value>The context.</value>
        public TaskProcessingContext Context
        {
            get { return context; }
        }
    }
}